package com.probox.common.entity.utils;

import org.apache.commons.codec.binary.Hex;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @author HY
 * @version 2019年1月16日 上午9:43:24
 * @category 类说明
 */

public class WechatOpenUploadDemo {
    private static final Logger logger = LoggerFactory.getLogger(WechatOpenUploadDemo.class);

    private final static String wechatKeyFile = "./resources/test_wechat.p12";


    /**
     * 上传时调用此方法
     *
     * @param filePath 图片路径
     * @param mch_id   商户号
     * @param appKey   商户key
     */

    public static void wxImageUpload(String filePath, String mch_id, String appKey) {
        try {
            String media_hash = md5HashCode(new FileInputStream(filePath));
            Map paramMap = new HashMap<>();//用来参与计算sign的参数
            paramMap.put("mch_id", mch_id);
            paramMap.put("media_hash", media_hash);
            paramMap.put("sign_type", "HMAC-SHA256");
            String sign = sha256Sign(paramMap, appKey);
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();//用来实际请求接口的参数
            multipartEntityBuilder.addTextBody("mch_id", mch_id, ContentType.MULTIPART_FORM_DATA);
            File file = new File(filePath);
            multipartEntityBuilder.addBinaryBody("media", file, ContentType.create("image/jpg"), file.getName());
            multipartEntityBuilder.addTextBody("media_hash", media_hash, ContentType.MULTIPART_FORM_DATA);
            multipartEntityBuilder.addTextBody("sign_type", "HMAC-SHA256", ContentType.MULTIPART_FORM_DATA);
            multipartEntityBuilder.addTextBody("sign", sign, ContentType.MULTIPART_FORM_DATA);
            FileInputStream instream = null;
            SSLContext sslcontext = null;
            try {
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                instream = new FileInputStream(new File(wechatKeyFile));
                keyStore.load(instream, mch_id.toCharArray());// 这里写密码..默认是你的MCHID
                sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
            } catch (Exception e) {
                logger.error("证书加载失败!{}", e);

            } finally {
                try {
                    if (instream != null) {
                        instream.close();
                    }
                } catch (IOException e) {
                    logger.error("证书加载失败!{}", e);
                }
            }
            @SuppressWarnings("deprecation")
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            try {
                HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/mch/uploadmedia");

                RequestConfig config = RequestConfig.custom().setConnectTimeout(10000).setConnectionRequestTimeout(10000).setSocketTimeout(10000).build();

                httpPost.setConfig(config);

                httpPost.addHeader("Content-Type", "multipart/form-data; charset=UTF-8");

                httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36");

                httpPost.setEntity(multipartEntityBuilder.build());

                CloseableHttpResponse response = httpclient.execute(httpPost);

                String result = EntityUtils.toString(response.getEntity(), "UTF-8");

                logger.info("请求返回结果：{}", result);

            } catch (Exception e) {
                logger.error("请求失败！{}", e);

            }

        } catch (Exception e) {
            e.printStackTrace();

        }

    }

    /**
     * 对上次文件进行MD5获取其Hash值
     *
     * @param fis
     * @return
     */

    public static String md5HashCode(InputStream fis) {
        try {
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fis.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }
            return new String(Hex.encodeHex(MD5.digest()));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 获取HMAC-SHA256签名
     *
     * @param paramMap 签名参数(sign不参与签名)
     * @param key      签名密钥
     * @return HMAC-SHA256签名结果
     */

    public final static String sha256Sign(Map paramMap, String key) {
        try {
            String payParam = getSignTemp(paramMap, key);
            logger.info("签名原文:{}", payParam);
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(payParam.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            String sign = sb.toString().toUpperCase();
            logger.info("签名结果:{}", sign);
            return sign;

        } catch (Exception e) {
            return null;

        }

    }

    /**
     * 获取签名参数字符串
     *
     * @param paramMap 签名参数(sign字段不参与签名)
     * @param payKey   签名密钥
     * @return 待签名字符串
     */

    private final static String getSignTemp(Map paramMap, String payKey) {
        ArrayList<String> keyList = new ArrayList<>(paramMap.keySet());
        Collections.sort(keyList);
        StringBuilder signParam = new StringBuilder();
        for (String key : keyList) {
            if (!"sign".equals(key) && paramMap.get(key) != null) {
                signParam.append(key).append("=").append(paramMap.get(key)).append("&");
            }
        }
        signParam.delete(signParam.length() - 1, signParam.length());
        signParam.append("&key=").append(payKey);
        return signParam.toString();

    }

}
